#include "BxDF.h"
#include "MonteCarloUtility.h"
#include "GlobalDefine.h"
#include "Utility.h"


namespace mray
{

inline bool SameHemisphere(const vec3& w, const vec3& wp)
{
	return w.z * wp.z > 0.f;
}

inline float AbsCosTheta(const vec3& w)
{
	return abs(w.z);
}

inline vec3 SphericalDirection(float sintheta, float costheta, float phi)
{
	float x = sin(phi)*costheta;
	float y = sin(phi)*sintheta;
	float z = cos(phi);
	return vec3(x,y,z);
}

}


float mray::BxDF::Sample(const vec3& wo, vec3& wi, const float u1, const float u2) const
{
	wi = CosineSampleHemisphere(u1, u2);
	if(wo.z < 0.f) wi.z *= -1.f;
	return SameHemisphere(wo, wi) ? AbsCosTheta(wi) * INV_PI : 0.f;
}

float mray::BxDF::Pdf(const vec3& wo, const vec3& wi) const
{
	return SameHemisphere(wo, wi) ? AbsCosTheta(wi) * INV_PI : 0.f;
}

float mray::BlinnBRDF::Sample(const vec3& wo, vec3& wi, const float u1, const float u2) const 
{
	float costheta = pow(u1, 1.f/(m_exponent+1.f));
	float sintheta = sqrt(Max(0.f, 1.f-costheta*costheta));
	float phi = u2 * 2.f * PI;
	vec3 wh = SphericalDirection(sintheta, costheta, phi);
	if(!SameHemisphere(wo, wh)) wh = -wh;
	float woDotwh = wo * wh;
	wi = -wo +  wh*(2.f*woDotwh);
	if(woDotwh <= 0.f) return 0.f;
	else return (m_exponent + 1.f) * pow(costheta, m_exponent) / (2.f*PI*4.f*woDotwh);
}

float mray::BlinnBRDF::Pdf(const vec3& wo, const vec3& wi) const
{
	vec3 wh = wo+wi;
	wh.normalize();
	float woDotwh = wo * wh;

	if(woDotwh <= 0.f)
		return 0.f;
	else
		return (m_exponent + 1.f) * pow(AbsCosTheta(wh), m_exponent) / (2.f*PI*4.f*woDotwh);
}
